package com.googlecode.imanager.client.value.mask;

import com.googlecode.imanager.client.event.KeyEvent;
import com.googlecode.imanager.client.event.KeyEventType;
import com.googlecode.imanager.client.util.HasTextSelection;


/**
 * @author wellsantos@wellsantos.com
 * @since 0.01
 */
public class SimpleMask implements InputMask {

    private static final long serialVersionUID = 1L;
    
    protected static final CharMatcher NUMBER_MATCHER = new CharMatcher() {
        @Override
        public boolean matches(char chr) {
            return (chr >= '0') && (chr <= '9');
        }
    };
    
    protected static final CharMatcher LETTER_MATCHER = new CharMatcher() {
        @Override
        public boolean matches(char chr) {
            return ((chr >= 'a') && (chr <= 'z'))
                || ((chr >= 'A') && (chr <= 'Z'));
        }
    };
    
    protected static final CharMatcher WORD_MATCHER = new CharMatcher() {
        @Override
        public boolean matches(char chr) {
            return ((chr >= 'a') && (chr <= 'z'))
                || ((chr >= 'A') && (chr <= 'Z'))
                || ((chr >= '0') && (chr <= '9'));
        }
    };
    
    protected final char inputChar;
    protected final String mask;
    protected transient String defaults;
    protected transient CharMatcher[] patterns;

    public SimpleMask(String mask) {
        this(mask, '_');
    }
    
    public SimpleMask(String mask, char inputChar) {
        this.mask = mask == null ? "" : mask;
        this.inputChar = inputChar;
    }
    
    protected void initMaskEngine(){
        if (patterns == null){
            int len = mask.length();
            char[] defaults = new char[len];
            
            patterns = new CharMatcher[len];
            
            for (int i=0; i<len; i++){
                switch (mask.charAt(i)) {
                    case '9':
                        defaults[i] = inputChar;
                        patterns[i] = NUMBER_MATCHER;
                        break;
                        
                    case 'a':
                    case 'A':
                        defaults[i] = inputChar;
                        patterns[i] = LETTER_MATCHER;
                        break;
                        
                    case 'w':
                    case 'W':
                        defaults[i] = inputChar;
                        patterns[i] = WORD_MATCHER;
                        break;
                        
                    default:
                        defaults[i] = mask.charAt(i);
                        patterns[i] = null;
                        break;
                }
            }
            
            this.defaults = new String(defaults);
        }
    }

    @Override
    public String maskText(String text) {
        initMaskEngine();
        
        StringBuilder value = new StringBuilder(text == null ? "" : text);        
        int length = value.length();
        int maxLen = patterns.length;

        MainLoop:
            
            for (int i=0; (i<length) && (i<maxLen); i++){
                char chr = value.charAt(i);

                if (defaults.charAt(i) == chr){
                    continue;
                }

                CharMatcher p = patterns[i];

                if (p != null){
                    if (p.matches(chr)){
                        continue;
                    }

                    for (int j=i+1; (j<length) && (j < maxLen); j++){
                        if (defaults.charAt(j) == chr){

                            while (i < j){
                                value.insert(i, defaults.charAt(i));
                                length++;
                                i++;
                            }

                            continue MainLoop;
                        }
                    }                
                } else {
                    for (int j=i+1; (j<length) && (j < maxLen); j++){
                        CharMatcher m = patterns[j];
                        if (m != null
                                && m.matches(chr)){

                            while (i < j){                            
                                value.insert(i, defaults.charAt(i));
                                length++;
                                i++;
                            }

                            value.setCharAt(i, chr);
                            continue MainLoop;
                        }
                    }                
                }

                value.delete(i, i+1);
                length--;
                i--;
            }

        if (length < maxLen){
            value.insert(length, defaults, length, maxLen);
        }
        else if (length > maxLen){
            value.delete(maxLen, length);
        }

        return value.toString();
    }

    @Override
    public void maskKeyEvent(HasTextSelection target, KeyEvent e) {
        if (e.getEventType() == KeyEventType.KEY_PRESSED){
            char chr = e.getKeyChar();
            
            if (chr != 0){
                initMaskEngine();
                
                String text = target.getText();
                int caret = target.getCaretPosition();
                int maxLen = patterns.length;
                
                if (text == null){
                    text = "";
                } 
                else if (maxLen > text.length()){
                    maxLen = text.length();
                }
                    
                if (caret >= maxLen){
                    if (caret >= patterns.length){
                        if (caret > patterns.length){
                            target.setText(maskText(text));
                        }
                        e.setPreventDefault(true);
                    }
                    return;
                }
             
                do {
                    CharMatcher p = patterns[caret];
                    if (p != null){
                        if (p.matches(chr)){
                            StringBuilder value = new StringBuilder(text);
                            value.setCharAt(caret, chr);
                            target.setText(value.toString());
                            target.setCaretPosition(caret+1);
                        } else {
                            if (chr == ' ' || chr == defaults.charAt(caret)){
                                StringBuilder value = new StringBuilder(text);
                                value.setCharAt(caret, defaults.charAt(caret));
                                target.setText(value.toString());
                                target.setCaretPosition(caret+1);
                            }
                        }
                    } else {
                        caret++;
                        continue;
                    }

                    e.setPreventDefault(true);
                    return;
                    
                } while (caret < maxLen);
                
                target.setCaretPosition(target.getCaretPosition()+1);
                e.setPreventDefault(true);
            }
        }
    }

    @Override
    public String getDefaultText() {
        initMaskEngine();
        return defaults;
    }

    public char getInputChar() {
        return inputChar;
    }

    public String getMask() {
        return mask;
    }
    
}
